package net.chengp.ms.gateway.config;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import lombok.extern.slf4j.Slf4j;
import net.chengp.ms.gateway.constants.GatewayConstant;
import reactor.core.publisher.Mono;

/**
 * 鉴权管理
 * @author chengp
 *
 */
@Component
@Slf4j
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
	
	private static final Cache<String, List<String>> rolesCache = CacheBuilder.newBuilder().maximumSize(Integer.MAX_VALUE)
			.expireAfterAccess(10, TimeUnit.SECONDS).build();
	
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        URI uri = authorizationContext.getExchange().getRequest().getURI();
        //获取当前路径可访问角色列表
        List<String> authorities = getUserDetails(uri.getPath());
        //认证通过且角色匹配的用户可访问当前路径
        return mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(authorities::contains)
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }
    
    
    private List<String> getUserDetails(String path) {
		try {
			return rolesCache.get(path, new Callable<List<String>>() {
				@Override
				public List<String> call() throws Exception {
					// TODO 这里需要从数据库查询用户
					return Arrays.asList(GatewayConstant.AUTHORITY_PREFIX+"ADMIN");
				}
			});
		} catch (Exception e) {
			log.error("查询用户失败!", e);
		}
		return null;
	}

}
